package cn.iocoder.yudao.module.energy.service.statistics.impl;

import cn.iocoder.yudao.module.energy.controller.admin.statistics.vo.EnergyStatisticsReqVO;
import cn.iocoder.yudao.module.energy.controller.admin.statistics.vo.EnergyStatisticsRespVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.device.EnergyDeviceDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.report.EnergyReportDataDO;
import cn.iocoder.yudao.module.energy.dal.mysql.device.EnergyDeviceMapper;
import cn.iocoder.yudao.module.energy.dal.mysql.report.EnergyReportDataMapper;
import cn.iocoder.yudao.module.energy.enums.report.EnergyReportDataLevelEnum;
import cn.iocoder.yudao.module.energy.service.statistics.EnergyStatisticsService;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 能源统计服务实现类
 */
@Service
@Validated
public class EnergyStatisticsServiceImpl implements EnergyStatisticsService {

    @Resource
    private EnergyDeviceMapper deviceMapper;

    @Resource
    private EnergyReportDataMapper reportDataMapper;

    private static final DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern("MM-dd");
    private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");

    @Override
    public EnergyStatisticsRespVO getEnergyStatistics(EnergyStatisticsReqVO reqVO) {
        EnergyStatisticsRespVO respVO = new EnergyStatisticsRespVO();
        
        // 查询时间范围
        LocalDateTime[] timeRange = getTimeRange(reqVO.getTimeRange());
        LocalDateTime startTime = timeRange[0];
        LocalDateTime endTime = timeRange[1];
        
        // 设备查询条件
        LambdaQueryWrapperX<EnergyDeviceDO> queryWrapper = new LambdaQueryWrapperX<>();
        if (reqVO.getDeviceId() != null && !reqVO.getDeviceId().isEmpty()) {
            queryWrapper.eq(EnergyDeviceDO::getDeviceId, reqVO.getDeviceId());
        }
        
        // 查询设备总数
        long deviceCount = deviceMapper.selectCount(queryWrapper);
        respVO.setDeviceCount((int) deviceCount);
        
        // 查询报告数据
        List<EnergyReportDataDO> reportDataList = getReportDataList(reqVO.getDeviceId(), startTime, endTime);
        
        // 统计数据
        BigDecimal totalEnergy = BigDecimal.ZERO;
        int todayReportCount = 0;
        int warningCount = 0;
        int dangerCount = 0;

        // 获取今天日期
        LocalDate today = LocalDate.now();
        LocalDateTime todayStart = today.atStartOfDay();
        LocalDateTime todayEnd = today.atTime(LocalTime.MAX);
        
        // 计算统计数据
        for (EnergyReportDataDO reportData : reportDataList) {
            // 累计能耗
            if (reportData.getEnergyConsumption() != null) {
                totalEnergy = totalEnergy.add(reportData.getEnergyConsumption());
            }
            
            // 今日数据
            LocalDateTime reportTime = reportData.getReportDataTime();
            if (reportTime != null && !reportTime.isBefore(todayStart) && !reportTime.isAfter(todayEnd)) {
                todayReportCount++;
            }
            
            // 警告和危险数据
            if (reportData.getReportDataLevel() != null) {
                if (EnergyReportDataLevelEnum.WARNING.getLevel().equals(reportData.getReportDataLevel())) {
                    warningCount++;
                } else if (EnergyReportDataLevelEnum.DANGER.getLevel().equals(reportData.getReportDataLevel())) {
                    dangerCount++;
                }
            }
        }
        
        respVO.setTotalEnergy(totalEnergy);
        respVO.setTodayReportCount(todayReportCount);
        respVO.setWarningCount(warningCount);
        respVO.setDangerCount(dangerCount);
        
        // 计算环比数据
        Map<String, BigDecimal> compareRates = new HashMap<>();
        
        // 上一周期的时间
        LocalDateTime[] prevTimeRange = getPreviousTimeRange(reqVO.getTimeRange());
        LocalDateTime prevStartTime = prevTimeRange[0];
        LocalDateTime prevEndTime = prevTimeRange[1];
        
        List<EnergyReportDataDO> prevReportDataList = getReportDataList(reqVO.getDeviceId(), prevStartTime, prevEndTime);
        
        // 上一周期统计数据
        BigDecimal prevTotalEnergy = BigDecimal.ZERO;
        int prevTodayReportCount = 0;
        int prevWarningCount = 0;
        int prevDangerCount = 0;
        
        for (EnergyReportDataDO reportData : prevReportDataList) {
            // 累计能耗
            if (reportData.getEnergyConsumption() != null) {
                prevTotalEnergy = prevTotalEnergy.add(reportData.getEnergyConsumption());
            }
            
            // 计算上一周期的对应时间内的报告数量
            LocalDateTime reportTime = reportData.getReportDataTime();
            if (reportTime != null) {
                if (isInSameDayOfPrevPeriod(reportTime, today, reqVO.getTimeRange())) {
                    prevTodayReportCount++;
                }
            }
            
            // 警告和危险数据
            if (reportData.getReportDataLevel() != null) {
                if (EnergyReportDataLevelEnum.WARNING.getLevel().equals(reportData.getReportDataLevel())) {
                    prevWarningCount++;
                } else if (EnergyReportDataLevelEnum.DANGER.getLevel().equals(reportData.getReportDataLevel())) {
                    prevDangerCount++;
                }
            }
        }
        
        // 计算环比变化率
        BigDecimal energyRate = calculateCompareRate(totalEnergy, prevTotalEnergy);
        BigDecimal reportCountRate = calculateCompareRate(new BigDecimal(todayReportCount), new BigDecimal(prevTodayReportCount));
        BigDecimal warningRate = calculateCompareRate(new BigDecimal(warningCount), new BigDecimal(prevWarningCount));
        BigDecimal dangerRate = calculateCompareRate(new BigDecimal(dangerCount), new BigDecimal(prevDangerCount));
        
        compareRates.put("energy", energyRate);
        compareRates.put("reportCount", reportCountRate);
        compareRates.put("warning", warningRate);
        compareRates.put("danger", dangerRate);
        
        respVO.setCompareRates(compareRates);
        
        // 设置趋势、环比和分布数据
        respVO.setTrendData(Arrays.asList(getEnergyTrend(reqVO)));
        respVO.setCompareData(Arrays.asList(getEnergyCompare(reqVO)));
        respVO.setDistributionData(Arrays.asList(getEnergyDistribution(reqVO)));
        
        return respVO;
    }

    @Override
    public EnergyStatisticsRespVO.EnergyTrendVO[] getEnergyTrend(EnergyStatisticsReqVO reqVO) {
        // 查询时间范围
        LocalDateTime[] timeRange = getTimeRange(reqVO.getTimeRange());
        LocalDateTime startTime = timeRange[0];
        LocalDateTime endTime = timeRange[1];
        
        // 获取报告数据
        List<EnergyReportDataDO> reportDataList = getReportDataList(reqVO.getDeviceId(), startTime, endTime);
        
        // 按照数据类型和时间分组
        Map<String, Map<String, List<EnergyReportDataDO>>> dataByTypeAndTime = new HashMap<>();
        
        for (EnergyReportDataDO reportData : reportDataList) {
            if (reportData.getReportDataTime() == null || reportData.getReportDataType() == null) {
                continue;
            }
            
            String dataType = reportData.getReportDataType();
            String timeStr = formatTimeForTrend(reportData.getReportDataTime(), reqVO.getTimeRange());
            
            // 初始化类型
            if (!dataByTypeAndTime.containsKey(dataType)) {
                dataByTypeAndTime.put(dataType, new HashMap<>());
            }
            
            // 初始化时间
            Map<String, List<EnergyReportDataDO>> timeMap = dataByTypeAndTime.get(dataType);
            if (!timeMap.containsKey(timeStr)) {
                timeMap.put(timeStr, new ArrayList<>());
            }
            
            // 添加数据
            timeMap.get(timeStr).add(reportData);
        }
        
        // 转换为结果
        List<EnergyStatisticsRespVO.EnergyTrendVO> result = new ArrayList<>();
        
        for (Map.Entry<String, Map<String, List<EnergyReportDataDO>>> typeEntry : dataByTypeAndTime.entrySet()) {
            String dataType = typeEntry.getKey();
            Map<String, List<EnergyReportDataDO>> timeMap = typeEntry.getValue();
            
            for (Map.Entry<String, List<EnergyReportDataDO>> timeEntry : timeMap.entrySet()) {
                String timeStr = timeEntry.getKey();
                List<EnergyReportDataDO> dataList = timeEntry.getValue();
                
                // 计算平均值
                BigDecimal avgValue = dataList.stream()
                        .filter(data -> data.getReportDataValue() != null)
                        .map(EnergyReportDataDO::getReportDataValue)
                        .reduce(BigDecimal.ZERO, BigDecimal::add)
                        .divide(new BigDecimal(Math.max(1, dataList.size())), 2, RoundingMode.HALF_UP);
                
                EnergyStatisticsRespVO.EnergyTrendVO trendVO = new EnergyStatisticsRespVO.EnergyTrendVO();
                trendVO.setTime(timeStr);
                trendVO.setDataType(dataType);
                trendVO.setValue(avgValue);
                
                result.add(trendVO);
            }
        }
        
        return result.toArray(new EnergyStatisticsRespVO.EnergyTrendVO[0]);
    }

    @Override
    public EnergyStatisticsRespVO.EnergyCompareVO[] getEnergyCompare(EnergyStatisticsReqVO reqVO) {
        // 查询当前时间范围
        LocalDateTime[] timeRange = getTimeRange(reqVO.getTimeRange());
        LocalDateTime startTime = timeRange[0];
        LocalDateTime endTime = timeRange[1];
        
        // 查询上一周期时间范围
        LocalDateTime[] prevTimeRange = getPreviousTimeRange(reqVO.getTimeRange());
        LocalDateTime prevStartTime = prevTimeRange[0];
        LocalDateTime prevEndTime = prevTimeRange[1];
        
        // 获取当前和上一周期报告数据
        List<EnergyReportDataDO> currentDataList = getReportDataList(reqVO.getDeviceId(), startTime, endTime);
        List<EnergyReportDataDO> prevDataList = getReportDataList(reqVO.getDeviceId(), prevStartTime, prevEndTime);
        
        // 按数据类型分组
        Map<String, List<EnergyReportDataDO>> currentDataByType = currentDataList.stream()
                .filter(data -> data.getReportDataType() != null)
                .collect(Collectors.groupingBy(EnergyReportDataDO::getReportDataType));
        
        Map<String, List<EnergyReportDataDO>> prevDataByType = prevDataList.stream()
                .filter(data -> data.getReportDataType() != null)
                .collect(Collectors.groupingBy(EnergyReportDataDO::getReportDataType));
        
        // 计算环比数据
        List<EnergyStatisticsRespVO.EnergyCompareVO> result = new ArrayList<>();
        
        // 合并所有类型
        Set<String> allTypes = new HashSet<>();
        allTypes.addAll(currentDataByType.keySet());
        allTypes.addAll(prevDataByType.keySet());
        
        for (String dataType : allTypes) {
            // 当前周期值
            BigDecimal currentValue = calculateAverageValue(currentDataByType.getOrDefault(dataType, Collections.emptyList()));
            
            // 上一周期值
            BigDecimal prevValue = calculateAverageValue(prevDataByType.getOrDefault(dataType, Collections.emptyList()));
            
            // 计算变化率
            BigDecimal changeRate = calculateCompareRate(currentValue, prevValue);
            
            EnergyStatisticsRespVO.EnergyCompareVO compareVO = new EnergyStatisticsRespVO.EnergyCompareVO();
            compareVO.setDataType(dataType);
            compareVO.setCurrentValue(currentValue);
            compareVO.setPreviousValue(prevValue);
            compareVO.setChangeRate(changeRate);
            
            result.add(compareVO);
        }
        
        return result.toArray(new EnergyStatisticsRespVO.EnergyCompareVO[0]);
    }

    @Override
    public EnergyStatisticsRespVO.EnergyDistributionVO[] getEnergyDistribution(EnergyStatisticsReqVO reqVO) {
        // 查询时间范围
        LocalDateTime[] timeRange = getTimeRange(reqVO.getTimeRange());
        LocalDateTime startTime = timeRange[0];
        LocalDateTime endTime = timeRange[1];
        
        // 获取报告数据
        List<EnergyReportDataDO> reportDataList = getReportDataList(reqVO.getDeviceId(), startTime, endTime);
        
        // 按设备ID分组
        Map<String, List<EnergyReportDataDO>> dataByDevice = reportDataList.stream()
                .filter(data -> data.getDeviceId() != null && data.getEnergyConsumption() != null)
                .collect(Collectors.groupingBy(EnergyReportDataDO::getDeviceId));
        
        // 获取设备信息
        List<EnergyDeviceDO> devices = deviceMapper.selectList();
        Map<String, String> deviceNames = devices.stream()
                .collect(Collectors.toMap(
                        EnergyDeviceDO::getDeviceId,
                        EnergyDeviceDO::getDeviceName,
                        (v1, v2) -> v1));
        
        // 计算每个设备的能耗总量
        List<EnergyStatisticsRespVO.EnergyDistributionVO> result = new ArrayList<>();
        
        for (Map.Entry<String, List<EnergyReportDataDO>> entry : dataByDevice.entrySet()) {
            String deviceId = entry.getKey();
            List<EnergyReportDataDO> dataList = entry.getValue();
            
            // 计算设备总能耗
            BigDecimal totalEnergy = dataList.stream()
                    .map(EnergyReportDataDO::getEnergyConsumption)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            // 设备名称
            String deviceName = deviceNames.getOrDefault(deviceId, deviceId);
            
            EnergyStatisticsRespVO.EnergyDistributionVO distributionVO = new EnergyStatisticsRespVO.EnergyDistributionVO();
            distributionVO.setName(deviceName);
            distributionVO.setValue(totalEnergy);
            
            result.add(distributionVO);
        }
        
        // 排序取前10
        result.sort((a, b) -> b.getValue().compareTo(a.getValue()));
        if (result.size() > 10) {
            result = result.subList(0, 10);
        }
        
        return result.toArray(new EnergyStatisticsRespVO.EnergyDistributionVO[0]);
    }

    // ========== 私有方法 ==========

    /**
     * 根据时间范围类型获取起止时间
     */
    private LocalDateTime[] getTimeRange(String timeRange) {
        LocalDateTime startTime;
        LocalDateTime endTime = LocalDateTime.now();
        LocalDate today = LocalDate.now();
        
        switch (timeRange) {
            case "day":
                // 当天
                startTime = LocalDateTime.of(today, LocalTime.MIN);
                break;
            case "week":
                // 本周
                startTime = LocalDateTime.of(today.minusDays(today.getDayOfWeek().getValue() - 1), LocalTime.MIN);
                break;
            case "month":
                // 本月
                startTime = LocalDateTime.of(today.withDayOfMonth(1), LocalTime.MIN);
                break;
            case "year":
                // 本年
                startTime = LocalDateTime.of(today.withMonth(1).withDayOfMonth(1), LocalTime.MIN);
                break;
            default:
                // 默认为近7天
                startTime = LocalDateTime.of(today.minusDays(6), LocalTime.MIN);
        }
        
        return new LocalDateTime[] { startTime, endTime };
    }
    
    /**
     * 获取上一个周期的时间范围
     */
    private LocalDateTime[] getPreviousTimeRange(String timeRange) {
        LocalDateTime[] currentRange = getTimeRange(timeRange);
        LocalDateTime startTime = currentRange[0];
        LocalDateTime endTime = currentRange[1];
        
        long periodDays = java.time.Duration.between(startTime, endTime).toDays();
        
        LocalDateTime prevStartTime;
        LocalDateTime prevEndTime;
        
        switch (timeRange) {
            case "day":
                // 昨天
                prevStartTime = startTime.minusDays(1);
                prevEndTime = endTime.minusDays(1);
                break;
            case "week":
                // 上周
                prevStartTime = startTime.minusWeeks(1);
                prevEndTime = endTime.minusWeeks(1);
                break;
            case "month":
                // 上月
                prevStartTime = startTime.minusMonths(1);
                prevEndTime = prevStartTime.plusDays(periodDays).withHour(23).withMinute(59).withSecond(59);
                break;
            case "year":
                // 上年
                prevStartTime = startTime.minusYears(1);
                prevEndTime = prevStartTime.plusDays(periodDays).withHour(23).withMinute(59).withSecond(59);
                break;
            default:
                // 默认为前一个相同长度的周期
                prevStartTime = startTime.minusDays(periodDays);
                prevEndTime = startTime.minusSeconds(1);
        }
        
        return new LocalDateTime[] { prevStartTime, prevEndTime };
    }
    
    /**
     * 根据条件查询报告数据
     */
    private List<EnergyReportDataDO> getReportDataList(String deviceId, LocalDateTime startTime, LocalDateTime endTime) {
        // TODO: 实现根据条件查询，假设有一个自定义的查询接口
        return reportDataMapper.selectByDeviceAndTimeRange(deviceId, startTime, endTime);
    }
    
    /**
     * 计算环比变化率
     */
    private BigDecimal calculateCompareRate(BigDecimal current, BigDecimal previous) {
        if (previous == null || previous.compareTo(BigDecimal.ZERO) == 0) {
            return current != null && current.compareTo(BigDecimal.ZERO) > 0 ? new BigDecimal(100) : BigDecimal.ZERO;
        }
        
        if (current == null) {
            return new BigDecimal(-100);
        }
        
        return current.subtract(previous)
                .multiply(new BigDecimal(100))
                .divide(previous, 2, RoundingMode.HALF_UP);
    }
    
    /**
     * 判断给定的时间是否在上个周期的同一天
     */
    private boolean isInSameDayOfPrevPeriod(LocalDateTime time, LocalDate currentDay, String timeRange) {
        switch (timeRange) {
            case "day":
                // 昨天
                return time.toLocalDate().equals(currentDay.minusDays(1));
            case "week":
                // 上周同一天
                return time.toLocalDate().equals(currentDay.minusWeeks(1));
            case "month":
                // 上月同一天
                LocalDate prevMonth = currentDay.minusMonths(1);
                int dayOfMonth = Math.min(currentDay.getDayOfMonth(), prevMonth.lengthOfMonth());
                return time.toLocalDate().equals(prevMonth.withDayOfMonth(dayOfMonth));
            case "year":
                // 上年同一天
                LocalDate prevYear = currentDay.minusYears(1);
                return time.toLocalDate().getDayOfYear() == currentDay.getDayOfYear()
                        && time.toLocalDate().getYear() == prevYear.getYear();
            default:
                return false;
        }
    }
    
    /**
     * 格式化时间用于趋势图
     */
    private String formatTimeForTrend(LocalDateTime time, String timeRange) {
        switch (timeRange) {
            case "day":
                // 按小时
                return String.format("%02d:00", time.getHour());
            case "week":
            case "month":
                // 按天
                return time.format(DAY_FORMATTER);
            case "year":
                // 按月
                return time.format(MONTH_FORMATTER);
            default:
                return time.format(DAY_FORMATTER);
        }
    }
    
    /**
     * 计算平均值
     */
    private BigDecimal calculateAverageValue(List<EnergyReportDataDO> dataList) {
        if (dataList.isEmpty()) {
            return BigDecimal.ZERO;
        }
        
        BigDecimal sum = dataList.stream()
                .filter(data -> data.getReportDataValue() != null)
                .map(EnergyReportDataDO::getReportDataValue)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        return sum.divide(new BigDecimal(dataList.size()), 2, RoundingMode.HALF_UP);
    }
} 